/*
 * CURLOPT_HEADER, 是设置body的内容中是否包含了响应报文的报头
 * 这和是否接收报头无关，！显然报头是肯定要接收的！
 * CURLOPT_HEADERFUNCTION, 是设置当开始接收响应报头时的回调函数
 * CURLOPT_HEADERFUNCTION 和 CURLOPT_HEADER 没有什么关系
 * CURLOPT_NOBODY，则是设置不要显示body的内容
 */

#include "gvj.h"
#include "poj_handler.h"
#include "http_response.h"

#include <iostream>
#include <vector>
#include <cstdio>

PojHandler::PojHandler(int curl_id, std::string cookie_dir, 
		int outpipe, int msgid, pthread_mutex_t *mutex_ptr): 
		out_pipe(outpipe), msg_id(msgid), out_pipe_mutex_ptr(mutex_ptr),
		current_task("-", "-", "-", "-", "-") {
	//curl_global_init(CURL_GLOBAL_ALL);
	mycurl = new MyCurl(curl_id, get_cookie_path(cookie_dir.data(), "poj", curl_id));
}

PojHandler::~PojHandler() {
	delete mycurl;
}

// 根据具体当前网站login时需要发送的信息，修改PSSData里面的内容即可
std::string PojHandler::login_data(const char *user_id, const char *passwd) {
	std::vector<PSS> data;
	data.push_back(std::make_pair("user_id1", user_id));
	data.push_back(std::make_pair("password1", passwd));
	data.push_back(std::make_pair("B1", "login"));
	data.push_back(std::make_pair("url", "/"));
	return args_encode(data); // 该函数在gvj.cpp
}

// 根据具体当前网站submit时需要发送的信息，修改PSSData里面的内容即可
std::string PojHandler::submit_data(const char *problem_id, const char *language, const char *src) {
	std::vector<PSS> data;
	data.push_back(std::make_pair("problem_id", problem_id));
	data.push_back(std::make_pair("language", find_language_id(language)));
	data.push_back(std::make_pair("source", src));
	data.push_back(std::make_pair("submit", "Submit"));
	return args_encode(data);
}

// 根据具体编程语言，返回Poj中对应的数字编号(string形式返回)
std::string PojHandler::find_language_id(const char *language) {
	for (int i = 0; i < Poj::n_language; i++) {
		if (!strcmp(Poj::poj_language[i], language)) 
			return itoa_string(i);
		//std::cout << Poj::poj_language[i] << " " << language << std::endl;
	}
	return std::string("-1");
}

// curl的回调函数，保存header，body
size_t PojHandler::save_http_contents(char *ptr, size_t size, size_t nmemb, void *user_data) {
	((std::string*)user_data)->append(ptr, size * nmemb);
	return size * nmemb;
}

int PojHandler::login(const char *user_id, const char *passwd, void *user_data) {
	printf("poj_handler(%d) login\n", mycurl->id);
	CURL* &curl = mycurl->curl;
	const char *cookie_path = mycurl->cookie_path.data();
	HttpResponse res; // http响应对象
	std::string str_data = PojHandler::login_data(user_id, passwd); 
	const char *data = str_data.data();	
	
	//cout << data << endl;

	// 重置
	curl_easy_reset(curl);
	// 防止多线程crash，原因不明
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	// 设置访问的URL
	curl_easy_setopt(curl, CURLOPT_URL, Poj::url_login);
	// 自动跳转
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
	// POST要传递的数据
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);	
	// 设置超时
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 20L);
	// 设置cookie
	curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_path);
	curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_path);
	

	// 设置接收到响应报头时的回调函数，并传递回调函数需要的参数
	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, PojHandler::save_http_contents);
	curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void*)(&res.header));
	
	// 设置接收到消息体时的回调函数，并传递回调函数需要的参数
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, PojHandler::save_http_contents);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)(&res.body));
	
	// 执行
	CURLcode curl_res = curl_easy_perform(curl);
	if (curl_res != CURLE_OK) 
		fprintf(stdin, "%s login error: %s\n", user_id, curl_easy_strerror(curl_res));
	
	// DEBUG //
	FILE *fp = fopen("login_page.html", "w");
	fprintf(fp, "%s\n", res.body.data());
	fclose(fp);
	// DEBUG //

	return curl_res == CURLE_OK ? 0 : curl_res;
}

int PojHandler::submit(PojTask &task) {
	printf("poj_handler(%d) submit\n", mycurl->id);
	current_task = task;
	CURL* &curl = mycurl->curl;
	const char *cookie_path = mycurl->cookie_path.data();
	
	HttpResponse res;
	
	std::string src = add_comment() + task.source;
	std::string str_data = PojHandler::submit_data(task.problem_id.data(), task.language.data(), src.data());
	const char *data = str_data.data();
	
	//cout << data << endl;
	
	// 防止多线程crash，原因不明
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
	// 设置访问的URL
	curl_easy_setopt(curl, CURLOPT_URL, Poj::url_submit);
	// 设置跳转
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
	// 设置POST的数据
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
	// 设置超时
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 20L);
	// 设置cookie
	curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_path);
	curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_path);

	// 设置接收到响应报头时的回调函数，并传递回调函数需要的参数
	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, PojHandler::save_http_contents);
	curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void*)(&res.header));
	
	// 设置接收到消息体时的回调函数，并传递回调函数需要的参数
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, PojHandler::save_http_contents);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)(&res.body));

	// 执行
	CURLcode curl_res = curl_easy_perform(curl);
	if (curl_res != CURLE_OK) 
		fprintf(stdin, "submit error: %s\n", curl_easy_strerror(curl_res));
	
	// DEBUG //
	//std::cout << "submit: HTTP Header:\n" << res.header << std::endl;
	//std::cout << "submit: HTTP Body:\n" << res.body << std::endl;
	FILE *fp = fopen("submit_page.html", "w");
	fprintf(fp, "%s\n", res.body.data());
	fclose(fp);
	// DEBUG //


	return curl_res == CURLE_OK ? 0 : curl_res;
}

void PojHandler::get_judge_result() {
	char user_id[10];
	sprintf(user_id, "gvj%d", mycurl->id);
	std::string stream = parse_html(user_id);

	current_result = "";
	current_result += current_task.socket_fd  + "|";
	current_result += current_task.oj_name    + "|";
	current_result += stream;
	char str[20];
	sprintf(str, "%d|", current_result.length());
	current_result  = std::string(str) + current_result;
	printf("%s\n", current_result.data());
}

int PojHandler::send_result_to_listen() {
	int length = current_result.length();
	int remain = current_result.length();
	const char *str = current_result.data();

	pthread_mutex_lock(out_pipe_mutex_ptr);
	while (true) {
		ssize_t nbytes = write(out_pipe, str + length - remain, remain);
		if (nbytes > 0)  remain -= nbytes;
		if (remain <= 0) break;
	}
	pthread_mutex_unlock(out_pipe_mutex_ptr);
	printf("(%d) write out_pipe return\n", mycurl->id);
	return 0;
}


